package com.atguigu.tingshu.payment.service.impl;

import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.model.payment.PaymentInfo;
import com.atguigu.tingshu.payment.config.WxPayV3Config;
import com.atguigu.tingshu.payment.service.PaymentInfoService;
import com.atguigu.tingshu.payment.service.WxPayService;
import com.atguigu.tingshu.payment.util.PayUtil;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.model.Transaction;
import io.seata.spring.annotation.GlobalTransactional;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class WxPayServiceImpl implements WxPayService {

    @Autowired
    private PaymentInfoService paymentInfoService;

    @Autowired
    private RSAAutoCertificateConfig rsaAutoCertificateConfig;


    @Autowired
    private WxPayV3Config wxPayV3Config;

    /**
     * 对接微信支付获取小程序拉起微信支付所需参数，用于微信支付进行购买订单，以及充值
     *
     * @param paymentType 支付类型：1301-订单 1302-充值
     * @param orderNo     订单编号
     * @return
     */
    @Override
    public Map<String, String> getMiniProgramPayParams(String paymentType, String orderNo) {
        try {
            //1.查询或保存本地交易记录（后期对账使用）
            PaymentInfo paymentInfo = paymentInfoService.savePaymentInfo(paymentType, orderNo);
            //2.判断本地交易记录状态 必须是未支付
            if (paymentInfo != null && SystemConstant.PAYMENT_STATUS_UNPAID.equals(paymentInfo.getPaymentStatus())) {
                //3.对接微信支付接口获取小程序所需支付参数
                //3.1 JSAPI支付和APP支付业务类对象
                JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(rsaAutoCertificateConfig).build();
                //3.2 创建预下单请求对象
                PrepayRequest request = new PrepayRequest();
                Amount amount = new Amount();
                //微信金额单位分：1分 所有支付金额为1分
                amount.setTotal(1);
                request.setAmount(amount);
                request.setAppid(wxPayV3Config.getAppid());
                request.setMchid(wxPayV3Config.getMerchantId());
                request.setDescription(paymentInfo.getContent());
                //设置回调地址：提供给微信调用，通知商户支付结果
                request.setNotifyUrl(wxPayV3Config.getNotifyUrl());
                //微信参数OutTradeNo：商户侧订单编号
                request.setOutTradeNo(paymentInfo.getOrderNo());
                //小程序未上线：微信官方只允许appId应用下开发者账户才有权限进行付款
                Payer payer = new Payer();
                payer.setOpenid("odo3j4qp-wC3HVq9Z_D9C0cOr0Zs");
                request.setPayer(payer);
                //3.3 调用微信预下单接口获取响应，解析获取五项参数
                PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(request);
                if (response != null) {
                    String timeStamp = response.getTimeStamp();
                    String nonceStr = response.getNonceStr();
                    String packageVal = response.getPackageVal();
                    String signType = response.getSignType();
                    String paySign = response.getPaySign();
                    //4.封装五项参数返回给客户端
                    Map<String, String> map = new HashMap<>();
                    map.put("timeStamp", timeStamp);
                    map.put("nonceStr", nonceStr);
                    map.put("package", packageVal);
                    map.put("signType", signType);
                    map.put("paySign", paySign);
                    return map;
                }
            }
        } catch (Exception e) {
            log.error("[支付服务]对接微信支付失败：{}", e);
            throw new RuntimeException(e);
        }
        return null;
    }

    @Autowired
    private RedisTemplate redisTemplate;


    /**
     * 用户微信支付成功后，微信服务端会回调接口进行支付结果通知
     *
     * @return
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public Map<String, String> wxPaySuccessNotify(HttpServletRequest request) {
        //1.进行参数验签-确保参数是微信服务端发起，传输过程中未被篡改
        //1.1 从请求头中获取回调通知请求的数据
        String signature = request.getHeader("Wechatpay-Signature");
        String serial = request.getHeader("Wechatpay-Serial");
        String nonce = request.getHeader("Wechatpay-Nonce");
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        String signaureType = request.getHeader("Wechatpay-Signature-Type");
        log.info("签名：{}，序列号：{}，随机数：{}，时间戳：{}，签名类型：{}", signature, serial, nonce, timestamp, signaureType);
        //1.2 基于通知参数构建请求参数对象
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(serial)
                .nonce(nonce)
                .signature(signature)
                .timestamp(timestamp)
                .body(PayUtil.readData(request))
                .build();
        //1.3 初始化通知解析对象用于验签及解析请求支付结果业务参数
        NotificationParser parser = new NotificationParser(rsaAutoCertificateConfig);
        //1.4 以支付通知回调为例，验签、解密并转换成 Transaction
        Transaction transaction = parser.parse(requestParam, Transaction.class);

        //2. 经过验签后做好幂等性处理-微信同样的通知可能会多次发送给商户系统
        if (transaction != null) {
            //2.1 获取交易对象中商户订单编号
            String orderNo = transaction.getOutTradeNo();
            //2.2 将订单编号采用set k v ex nx 设置25小时时间
            String key = RedisConstant.BUSINESS_PREFIX + ":wxnotify:" + orderNo;
            Boolean flag = redisTemplate.opsForValue().setIfAbsent(key, null, 25, TimeUnit.HOURS);
            try {
                if (flag) {
                    //3.校验通知的信息(付款信息)是否与商户侧的信息一致
                    if (Transaction.TradeStateEnum.SUCCESS == transaction.getTradeState()) {
                        //3.1 获取到用户支付金额
                        Integer payerTotal = transaction.getAmount().getPayerTotal();
                        //3.2 TODO常规从本地校验记录中获取应付金额，测试：判断1分
                        if (payerTotal.intValue() == 1) {
                            //4. 修改本地交易记录信息
                            paymentInfoService.updatePaymentInfo(orderNo, transaction);
                        }
                    }
                }
            } catch (Exception e) {
                redisTemplate.delete(key);
                log.error("【支付系统】异步回调异常：{}", e);
                throw new RuntimeException(e);  //抛出异常，商户未正常应答结果，微信再次通知
            }
        }
        return null;
    }

    /**
     * 查询订单支付状态:调用微信支付接口查询支付状态
     *
     * @param orderNo
     * @return
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public Boolean queryPayStatus(String orderNo) {
       /* //1 JSAPI支付和APP支付业务类对象
        JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(rsaAutoCertificateConfig).build();
        //2.创建商户订单查询支付结果请求对象
        QueryOrderByOutTradeNoRequest queryRequest = new QueryOrderByOutTradeNoRequest();
        queryRequest.setMchid(wxPayV3Config.getMerchantId());
        queryRequest.setOutTradeNo(orderNo);

        try {
            //3.执行查询
            Transaction transaction = service.queryOrderByOutTradeNo(queryRequest);
            if (transaction != null) {
                if (transaction.getTradeState() == Transaction.TradeStateEnum.SUCCESS) {
                    Integer payerTotal = transaction.getAmount().getPayerTotal();
                    if (1 == payerTotal) {
                        return true;
                    }
                }
            }
        } catch (Exception e) {
            // API返回失败, 例如ORDER_NOT_EXISTS
            log.error("[支付系统]同步查询支付状态异常：{}", e);
        }
        return false;*/

        //同学们代码 前提：用户付款成功（假装）商户账户入账
        //1.伪造一个微信支付交易对象
        Transaction transaction = new Transaction();
        transaction.setTransactionId("wx"+IdUtil.getSnowflakeNextIdStr());
        transaction.setOutTradeNo(orderNo);
        transaction.setTradeState(Transaction.TradeStateEnum.SUCCESS);
        //2.调用更新本地交易记录业务方法
        paymentInfoService.updatePaymentInfo(orderNo, transaction);
        return true;
    }
}
